package de.jonasblatt.networkapi.rpi.network.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import de.jonasblatt.networkapi.rpi.network.commands.ClientCommand;
import de.jonasblatt.networkapi.rpi.network.commands.Command;

public class ServerWorker implements Runnable {
	private int clientId;

	private Socket clientSocket;
	private ObjectInputStream reader;

	private Server server;
	private ServerLog serverLog;
	/**
	 * stopFlag is used, since a Thread shouldn't be stopped via thread.stop().
	 * Instead this flag signals the thread to return from run()-Method.
	 */
	private boolean stopFlag;

	private ObjectOutputStream writer;

	public ServerWorker(Socket client, Server gs, ServerLog serverLog) {
		this.clientSocket = client;
		this.serverLog = serverLog;
		this.server = gs;
		this.stopFlag = false;

		this.reader = null;
		this.writer = null;
		try {
			this.reader = new ObjectInputStream(
					this.clientSocket.getInputStream());
			this.writer = new ObjectOutputStream(
					this.clientSocket.getOutputStream());
		} catch (IOException e) {
			this.serverLog
					.addMessage("Server in or out failed. Sserver closing..");
			this.closeConnection();
		}
	}

	/**
	 * Sets the stopFlag, which signalises the executing thread to halt. Closes
	 * the connection and removes itself as the server's observer
	 */
	public void closeConnection() {
		this.stopFlag = true;
		this.server.remove(this);
		try {
			this.clientSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public int getClientId() {
		return this.clientId;
	}

	/**
	 * The run()-Method initialises the object-streams. After Initialisation it
	 * waits to receive a command from the connected client(ClientCommand). If a
	 * ClientCommand is received, the server gets a notification with this
	 * command. If the client isn't connected anymore an IOException is thrown
	 * and the connection is closed from this side as well.
	 */
	@Override
	public void run() {
		while (true) {
			if (this.stopFlag) {
				return;
			}
			try {
				Command cmd = (Command) this.reader.readObject();
				if (cmd == null) {
					this.clientSocket.close();
					this.server.closeConnection();
					return;
				}
				if (cmd instanceof ClientCommand) {
					this.server.notify((ClientCommand) cmd);
				}
			} catch (IOException e) {
				this.serverLog.addMessage("Close connection to client "
						+ this.clientId + ".");
				this.closeConnection();
				return;
				// System.exit(-1);
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				this.serverLog
						.addMessage("Could not run service due to ClassNotFoundException.");
			}
		}
	}

	public void setClientId(int clientId) {
		this.clientId = clientId;
	}

	/**
	 * sends a ServerCommand to the connected Client. If the client isn't
	 * connected anymore an IOException is thrown and the connection is closed
	 * from this side as well.+-
	 *
	 * @param msg
	 */
	public boolean update(Command msg) {
		try {
			this.writer.writeObject(msg);
			this.writer.flush();
			return true;
		} catch (IOException e) {
			this.serverLog
					.addMessage("Couldn't update due to IOException at Server. Closing connection to client "
					+ this.clientId + ".");
			this.closeConnection();
			return false;
		}
	}

}
